#!/usr/bin/env python
#-*- coding:utf-8 -*-
'''线性消元性
'''

import numpy as np

def float_eq(a, b, EPS=0.00001):
    try:
        return np.abs(a-b)<EPS
    except:
        return np.max(np.abs(np.array(a)-b))<EPS

class Matrix(object):
    if 0:
        A=np.array(0)
    def __init__(self, A):
        A = np.asarray(A, dtype=float)
        assert isinstance(A, np.ndarray)
        assert(len(A.shape)==2)
        self.A = A
        self.__rows, self.__cols = A.shape

    def solve(self):
        A = self.A.copy()
        rows = self.__rows
        # 消去下三角
        for i in range(rows):
            # 选择主元,当前对角线上主元不为0
            self.selectRowPivot(A, i)
            if not self.isPivotRow(A, i):
                continue
            self.scaling(A, i, 1/A[i][i])
            for j in range(i+1, rows):
                self.replacement(A, j, i, -A[j][i])
            print(A)
        # 消去上三角
        n = rows-1
        if not self.isPivotRow(A, n):
            return False, A
        
        for i in range(n, 0, -1):
            for j in range(i-1, -1, -1):
                self.replacement(A, j, i, -A[j][i])
        return True, A

    def isPivotRow(self, A, r):
        return not float_eq(A[r][r],0)

    def selectRowPivot(self, A, r):
        if self.isPivotRow(A, r):
            return True
        for r1 in range(r+1, self.__rows):
            if not float_eq(A[r1][r],0):
                self.interchange(A, r, r1)
                return True

    def isValidRow(self, r):
        return 0 <= r < self.__rows

    def isValidCol(self, c):
        return 0 <= c < self.__cols
    
    def replacement(self, A, r0, r1, c):
        assert c is not None
        assert self.isValidRow(r0)
        assert self.isValidRow(r1)
        A[r0] += A[r1] * c
    
    def interchange(self, A, r0, r1):
        assert self.isValidRow(r0)
        assert self.isValidRow(r1)
        A[r0], A[r1] = A[r1], A[r0].copy()
    
    def scaling(self, A, r, c):
        assert self.isValidRow(r)
        A[r] *= c

def linearElimination(A):
    A = np.asarray(A, dtype=float)
    assert isinstance(A, np.ndarray)
    assert(len(A.shape)==2)
    

def main():
    A=[
        [1, -2, 1, 0],
        [0, 2, -8, 8],
        [5, 0, -5, 10]
    ]
    A = [
        [0, 1, -4, 8],
        [2, -3, 2, 1],
        [4, -8, 12, 1]
    ]
    A = [
        [1, 4, -2, 8, 12],
        [0, 1, -7, 2, -4],
        [0, 0, 5, -4, 7],
        [0, 0, 1, 3, -5]
    ]
    A = [
        [1, 5, 7],
        [-2, -7, -5]
    ]
    A = [
        [2, 4, -4],
        [5, 7, 11]
    ]
    A = [
        [1, -2, -2],
        [1, 5, 7]
    ]
    A = [
        [1, -5, 1],
        [3, -7, 5]
    ]
    A = [
        [1,7,3,-4],
        [0,1,-1,3],
        [0,0,0,1],
        [0,0,1,-2]
    ]
    A = [
        [1, -4, 9, 0],
        [0, 1, 7, 0],
        [0, 0, 2, 0]
    ]
    A = [
        [1, -1, 0, 0, -4],
        [0, 1, -3, 0, -7],
        [0, 0, 1, -3, -1],
        [0, 0, 0, 2, 4]
    ]
    A = [
        [1, -4, 1],
        [2, -1, -3],
        #[-1, -3, 4]
    ]
    matrix = Matrix(A)
    res, B = matrix.solve()
    print(res)
    print(np.round(B, 6))

if __name__ == '__main__':
    main()